'''
Provides a parser to the xml file with experiment configurations

Created on Mar 28, 2014

@author: Ricardo Grunitzki

'''
import os, random

import sumolib
from sumolib.net.edge import Edge
import xml.etree.ElementTree as ET


def str_to_bool(value):
    if value in ['True', 'true']:
        return True
    return False
    
class ParamsConfiguration(object):
    '''
    Parses the .xml file with the configs
    '''

    def __init__(self, cfgpath):
        '''
        Constructor
        :param cfgpath: path to config.xml
        :type cfgpath: string 
        
        '''
        self.cfgdir = os.path.dirname(os.path.realpath(cfgpath))
        self._set_defaults()
        cfgtree = ET.parse(cfgpath)
        root = cfgtree.getroot()
        
        print 'Parsing config file...'
        
        for io_element in root.find('input-output'):
            #TODO: check if io_element refers to absolute path            
            
            if io_element.tag == 'net-name':
                self.net_name = (io_element.get('value'))
                
            if io_element.tag == 'net-directory':
                self.net_directory = (io_element.get('value'))
            
            if io_element.tag == 'cfg-file':
                True#self.cfg_file = self._parse_path(io_element.get('value'))
                
            if io_element.tag == 'net-file':
                True#self.netfile = self._parse_path(io_element.get('value'))
                
            if io_element.tag == 'route-file':
                True#self.route_file = self._parse_path(io_element.get('value'))
                
            if io_element.tag == 'results-directory':
                self.results_directory = self._parse_path(io_element.get('value'))
                
        for param_element in root.find('sumo'):
            if param_element.tag == 'use-gui': 
                self.use_gui = str_to_bool(param_element.get('value'))
            
            if param_element.tag == 'sumo-path': 
                self.sumo_path = str(param_element.get('value'))
            
            if param_element.tag == 'traci-port': 
                self.traci_port = int(param_element.get('value'))
            
            if param_element.tag == 'random-seed': 
                self.random_seed = int(param_element.get('value'))
                
                #random number initialization
                random.seed(self.random_seed)
                
            if param_element.tag == 'auto-start':
                self.auto_start = str_to_bool(param_element.get('value'))
           
            if param_element.tag == 'no-warnings':
                self.no_warnings = str_to_bool(param_element.get('value'))
            
            if param_element.tag == 'ignore-accidents':
                self.ignore_accidents = str_to_bool(param_element.get('value'))
            
            if param_element.tag == 'ignore-route-errors':
                self.ignore_route_errors = str_to_bool(param_element.get('value'))
            
            if param_element.tag == 'time-to-teleport':
                self.time_to_teleport = int(param_element.get('value'))
            if param_element.tag == 'time-begin':
                self.time_begin = int(param_element.get('value'))
            if param_element.tag == 'time-end':
                self.time_end = str(param_element.get('value'))                    
                
        for sumo_element in root.find('route-choice'):
            if sumo_element.tag == 'alpha':
                self.alpha = float(sumo_element.get('value'))
            
            if sumo_element.tag == 'gamma':
                self.gamma = float(sumo_element.get('value'))
            
            if sumo_element.tag == 'epsilon-initial':
                self.epsilon_initial = float(sumo_element.get('value'))
                
            if sumo_element.tag == 'epsilon-decay-rate':
                self.epsilon_decay_rate = float(sumo_element.get('value'))
            
            if sumo_element.tag == 'max-step':
                self.max_step = int(sumo_element.get('value'))
                
            if sumo_element.tag == 'max-episode':
                self.max_episode = int(sumo_element.get('value'))
            
            if sumo_element.tag == 'en-route':
                self.en_route = str_to_bool(sumo_element.get('value'))
            
            if sumo_element.tag == 'k-shortest-paths':
                self.k_shortest_path = int(sumo_element.get('value'))
        
        
        #xml files
        #cfg.xml
        self.cfg_file = self._parse_path(os.path.join(self.net_directory, self.net_name, (self.net_name +'.cfg.xml')))
        #net.xml
        self.net_file = self._parse_path(os.path.join(self.net_directory, self.net_name, (self.net_name+'.net.xml')))
        #rou.xml
        self.route_file = self._parse_path(os.path.join(self.net_directory, self.net_name, (self.net_name+'.rou.xml')))
        
        #sumolib network
        self.network = sumolib.net.readNet(self.net_file)
        self.edges_id = dict([(edge.getID(), True) for edge in self.network.getEdges()])
        
        #exploration rate
        self.epsilon = self.epsilon_initial;

    def _parse_path(self, value):
        return os.path.join(self.cfgdir, os.path.expanduser(value))
    
    def get_sumo_command_line(self):
        command_line = "";
        if self.use_gui:
            command_line += "sumo-gui "
        else:
            command_line +="sumo "
            command_line +="--no-step-log "
            #disable xml validation
            command_line += "-X never "
        #configuration file
        command_line += "-c " + self.cfg_file + " "
        
        #traci port
        command_line += "--remote-port " + str(self.traci_port) + " "
        
        #seed
        command_line += "--seed " + str(self.random_seed) + " "
        
        #quit on end
        if self.quite_on_end:
            command_line += "-Q "
        
        #auto start
        if self.auto_start:
            command_line += "-S "
        #ignore warnings
        if self.no_warnings:
            command_line += "-W "
        #ignore accidents
        if self.ignore_accidents:
            command_line += "--ignore-accidents "
        #ignore route errors
        if self.ignore_route_errors:
            command_line += "--ignore-route-errors "
        #time to teleport
        command_line +="--time-to-teleport " + str(self.time_to_teleport) + " "
        
        #begin time
        if self.time_begin != "":
            command_line += "-b " + str(self.time_begin) + " "
        
        #time end
        if self.time_end != "":
            command_line += "-e " + str(self.time_end) + " "
        
        #redirect error logs
        command_line += "2> log.txt "
        
        #unlock terminal
        command_line += "&"
        
        return command_line
            
    def _set_defaults(self):
        
        #input-output group
        self.cfg_file = None
        self.net_file = None
        self.net_directory = None
        self.net_name = None
        self.route_file = None
        self.results_directory = None
        
        #sumo parameters
        self.use_gui = False;
        self.sumo_path = None
        self.traci_port = 8803
        self.random_seed = 100
        random.seed(self.random_seed)
        self.quite_on_end = True
        self.auto_start = True
        self.no_warnings = False
        self.ignore_accidents = False
        self.ignore_route_errors = False
        self.time_to_teleport = 200
        self.time_begin = 0
        self.time_end = 0
        
        #route choice parameters
        self.alpha = 0.05
        self.gamma = 0.9
        self.epsilon_initial = 1
        self.epsilon_decay_rate = 0.92
        self.epsilon = self.epsilon_initial
        self.max_step = 10000;
        self.max_episode = 150
        self.en_route = True
        self.k_shortest_path = 4    
    
    def random(self):
        return random